﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Xml.Linq;
using System.Xml.Serialization;
using mediaPrime.Logger;

namespace mediaPrime.Sdk
{
    public static class Settings
    {
        private static AppsList apps = new AppsList();
        private static bool isLoading = false;
        private static bool isSaving = false;

        internal static readonly string Version = "1";
        internal static string FileName = String.Empty;

        internal static event SettingChangedEventHandler SettingChanged;

        /// <summary>
        /// Contains all Plugins and mediaPrime with their DefaultSettings and UserSettings
        /// </summary>
        internal static AppsList Apps
        {
            get { return apps; }
            set { apps = value; }
        }

        /// <summary>
        /// Checks whether no settings differ from default settings
        /// </summary>
        internal static bool IsSettingsAreEqualDefaults
        {
            get
            {
                foreach (var app in apps)
                {
                    if (!app.IsSettingsAreEqualDefaults)
                    {
                        return false;
                    }
                }

                return true;
            }
        }

        /// <summary>
        /// Fires when a setting was changed
        /// </summary>
        /// <param name="appID">Plugin ID or mediaPrime</param>
        /// <param name="setting">The name of the changed setting</param>
        internal static void OnSettingChanged(string appID, string setting)
        {
            var settingChanged = SettingChanged;
            if (settingChanged != null)
                settingChanged(null, new SettingChangedEventArgs() { AppId = appID, Setting = setting });
        }

        /// <summary>
        /// Loads default settings (DefaultSettings.xml)
        /// </summary>
        /// <param name="fileName">Patht to DefaultSettings.xml</param>
        internal static void LoadDefaultSettings(string fileName, string appId)
        {
            isLoading = true;
            Apps[appId].LoadDefaultSettings(fileName);
            isLoading = false;
        }

        /// <summary>
        /// Loads user settings (UserSettings.xml)
        /// </summary>
        /// <param name="fileName">Path to UserSettings.xml</param>
        internal static void LoadUserSettings(string fileName)
        {
            isLoading = true;
            FileName = fileName;
            Apps.LoadUserSettings(fileName);
            isLoading = false;
        }

        /// <summary>
        /// Saves user settings
        /// </summary>
        /// <param name="fileName">Path to UserSettings.xml</param>
        /// <returns></returns>
        internal static bool Save(string fileName)
        {
            if (isLoading) return false;
            if (isSaving) return false;

            if (String.IsNullOrEmpty(FileName))
            {
                Log.Error("Settings.Save() FileName is null or empty!");
                return false;
            }

            isSaving = true;

            var returnValue = Apps.Save(fileName);

            isSaving = false;

            return returnValue;
        }

        internal static bool Save()
        {
            return Save(FileName);
        }


        internal static string GetLocalizedString(XElement localizationNode, string key)
        {
            string language = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToLower();

            return GetLocalizedString(localizationNode, key, language);
        }

        internal static string GetLocalizedString(XElement localizationNode, string key, string language)
        {
            if (localizationNode == null) return key;
            if (string.IsNullOrEmpty(language))
                language = "en";

            language = language.ToLower();

            string localizedString = (
                                         from resources in localizationNode.Elements("Resources")
                                         where resources != null
                                         let resAttribute = resources.Attribute("lang")
                                         where resAttribute != null
                                         let resValue = resAttribute.Value
                                         where resValue != null
                                         where resValue.ToLower() == language
                                         from resource in resources.Elements("Resource")
                                         where resource != null
                                         let keyAttribute = resource.Attribute("key")
                                         where keyAttribute != null
                                         where keyAttribute.Value == key
                                         let textAttribute = resource.Attribute("text")
                                         where textAttribute != null
                                         select textAttribute.Value
                                     ).FirstOrDefault();


            if (string.IsNullOrEmpty(localizedString))
            {
                if (language.ToLower() != "en")
                    return GetLocalizedString(localizationNode, key, "en");
                else
                    return key;
            }
            else
                return localizedString;
        }
    }

    public class Setting : INotifyPropertyChanged
    {
        private string key;
        private string settingValue;
        private Type type;

        internal string AppID { get; set; }

        /// <summary>
        /// Setting's name
        /// </summary>
        public string Key
        {
            get { return key; }
            set
            {
                key = value;
                if (value != null)
                    Settings.Save();
            }
        }

        /// <summary>
        /// Setting's value (serialized)
        /// </summary>
        public string Value
        {
            get { return settingValue; }
            set
            {
                bool changed = false;
                if (settingValue != value)
                    changed = true;

                settingValue = value;
                if (value != null)
                    Settings.Save();

                if (changed)
                {
                    Settings.OnSettingChanged(AppID, Key);
                    onPropertyChanged("Value");
                    onPropertyChanged("ConvertedValue");
                }
            }
        }

        /// <summary>
        /// Setting's value (deserialized)
        /// </summary>
        public object ConvertedValue
        {
            get
            {
                return GetValue();
            }
            set
            {

                SetValue(value);
            }
        }


        /// <summary>
        /// Setting's type (e.g. int, string, bool, etc.)
        /// </summary>
        public string Type { get; set; }

        /// <summary>
        /// Setting's display name
        /// </summary>
        public string DisplayName { get; set; }
        /// <summary>
        /// Setting's group name
        /// </summary>
        public string GroupName { get; set; }
        /// <summary>
        /// Setting's associated group
        /// </summary>
        public string Group { get; set; }
        /// <summary>
        /// Setting's scope (visibility)
        /// </summary>
        public SettingScope Scope { get; set; }
        /// <summary>
        /// Available values instead of Value. Used by SettingEnumValue
        /// </summary>
        public List<SettingEnumValue> Values { get; set; }
        /// <summary>
        /// Serialized version of Values
        /// </summary>
        internal string values { get; set; }

        public Setting()
        {
            Value = null;
            Key = null;
            Values = new List<SettingEnumValue>();
            Group = string.Empty;
            Scope = SettingScope.Standard;
        }

        public object GetValue()
        {
            switch (Type)
            {
                case "enum":
                case "Enum":
                case "EnumValue":
                case "System.Windows.Media.Color":
                case "Color":
                case "System.Drawing.Color":
                case "StringList":
                case "List<string>":
                case "List<String>":
                case "Brush":
                case "Dictionary":
                case "Font":
                case "String":
                case "string": return GetValue(null);
                case "uint": return GetValue(0);
                case "double": return GetValue(-1.0);
                case "int": return GetValue(-1);
                case "bool": return GetValue(false);

                default: return null;
            }
        }

        public Type GetSettingsType()
        {
            switch (Type)
            {
                case "System.Windows.Media.Color": return typeof(System.Windows.Media.Color);
                case "Color":
                case "System.Drawing.Color": return typeof(System.Drawing.Color);
                case "StringList": return typeof(List<string>);
                case "List<string>": return typeof(List<string>);
                case "List<String>": return typeof(List<String>);
                case "String": return typeof(String);
                case "string": return typeof(string);
                case "uint": return typeof(uint);
                case "double": return typeof(double);
                case "int": return typeof(int);
                case "bool": return typeof(bool);
                case "enum":
                case "Enum":
                case "EnumValue": return typeof(SettingEnumValue);
                case "Brush": return typeof(Brush);
                case "Dictionary": return typeof(Dictionary<string, string>);
                case "Font": return typeof(SettingFont);

                default: return typeof(object);
            }
        }

        /// <summary>
        /// Returns true, if settings type is one of the default settings types supported by mediaPrime
        /// </summary>
        /// <returns></returns>
        public bool IsDefaultSettingsType
        {
            get
            {
                switch (Type)
                {
                    case "enum":
                    case "Enum":
                    case "EnumValue":
                    case "System.Windows.Media.Color":
                    case "Color":
                    case "System.Drawing.Color":
                    case "StringList":
                    case "List<string>":
                    case "List<String>":
                    case "Brush":
                    case "Dictionary":
                    case "Font":
                    case "String":
                    case "string":
                    case "uint":
                    case "double":
                    case "int":
                    case "bool": return true;

                    default: return false;
                }
            }
        }

        internal void SetSettingsType()
        {
            if (!string.IsNullOrEmpty(Type)) return;

            if (type == typeof(System.Windows.Media.Color))
            {
                Type = "System.Windows.Media.Color";
            }
            else if (type == typeof(System.Drawing.Color))
            {
                Type = "Color";
            }
            else if (type == typeof(List<string>))
            {
                Type = "List<string>";
            }
            else if (type == typeof(List<String>))
            {
                Type = "List<String>";
            }
            else if (type == typeof(String))
            {
                Type = "String";
            }
            else if (type == typeof(string))
            {
                Type = "string";
            }
            else if (type == typeof(uint))
            {
                Type = "uint";
            }
            else if (type == typeof(double))
            {
                Type = "double";
            }
            else if (type == typeof(int))
            {
                Type = "int";
            }
            else if (type == typeof(bool))
            {
                Type = "bool";
            }
            else if (type == typeof(SettingEnumValue))
            {
                Type = "enum";
            }

            else if (typeof(Brush).IsAssignableFrom(type))
                Type = "Brush";

            else if (type == typeof(Dictionary<string, string>))
                Type = "Dictionary";

            else if (type == typeof(SettingFont))
                Type = "Font";
            else
            {
                Type = "string";
            }
        }

        public object GetValue(object defaultValue)
        {
            if (string.IsNullOrEmpty(Type))
                return defaultValue;

            switch (Type)
            {
                case "System.Windows.Media.Color":
                case "Color": return GetValue<System.Windows.Media.Color>(defaultValue);
                case "System.Drawing.Color": return GetValue<System.Drawing.Color>(defaultValue);
                case "StringList":
                case "List<string>": return GetValue<List<string>>(defaultValue);
                case "List<String>": return GetValue<List<String>>(defaultValue);
                case "String": return GetValue<String>(defaultValue);
                case "string": return GetValue<string>(defaultValue);
                case "uint": return GetValue<uint>(defaultValue);
                case "double": return GetValue<double>(defaultValue);
                case "int": return GetValue<int>(defaultValue);
                case "bool": return GetValue<bool>(defaultValue);
                case "enum":
                case "Enum":
                case "EnumValue": return GetValue<SettingEnumValue>(defaultValue);
                case "Brush": return GetValue<Brush>(defaultValue);
                case "Dictionary": return GetValue<Dictionary<string, string>>(defaultValue);
                case "Font": return GetValue<SettingFont>(defaultValue);
                default: return null;
            }
        }

        public T GetValue<T>()
        {
            if (typeof(T) == typeof(int))
                return (T)(object)GetValue<int>(-1);
            else if (typeof(T) == typeof(uint))
                return (T)(object)GetValue<uint>(0);

            else if (typeof(T) == typeof(string))
                return (T)(object)GetValue<string>(null);

            else if (typeof(T) == typeof(String))
                return (T)(object)GetValue<String>(null);

            else if (typeof(T) == typeof(double))
                return (T)(object)GetValue<double>(-1.0);

            else if (typeof(T) == typeof(bool))
                return (T)(object)GetValue<bool>(false);

            else if (typeof(T) == typeof(System.Windows.Media.Color))
                return (T)(object)GetValue<System.Windows.Media.Color>(null);

            else if (typeof(T) == typeof(System.Drawing.Color))
                return (T)(object)GetValue<System.Drawing.Color>(null);

            else if (typeof(T) == typeof(List<string>))
                return (T)(object)GetValue<List<string>>(null);

            else if (typeof(T) == typeof(List<String>))
                return (T)(object)GetValue<List<String>>(null);

            else if (typeof(T) == typeof(SettingEnumValue))
                return (T)(object)GetValue<SettingEnumValue>(null);
            else if (typeof(T) == typeof(Brush))
                return (T)(object)GetValue<Brush>(null);
            else if (typeof(T) == typeof(Dictionary<string, string>))
                return (T)(object)GetValue<Dictionary<string, string>>(null);
            else if (typeof(T) == typeof(SettingFont))
                return (T)(object)GetValue<SettingFont>(null);
            else
                throw new NotSupportedException("Not supported type!");
        }

        public T GetValue<T>(object defaultValue)
        {
            try
            {
                if (typeof(T) == typeof(int))
                {
                    int result = int.Parse(Value);
                    return (T)(object)result;

                }
                else if (typeof(T) == typeof(uint))
                {
                    uint result = uint.Parse(Value);
                    return (T)(object)result;

                }

                else if (typeof(T) == typeof(string))
                {
                    string returnValue = defaultValue as string;
                    if (Value != null)
                        returnValue = Value;

                    return (T)(object)returnValue;
                }

                else if (typeof(T) == typeof(String))
                {
                    String returnValue = defaultValue as String;
                    if (Value != null)
                        returnValue = Value;

                    return (T)(object)returnValue;
                }

                else if (typeof(T) == typeof(List<string>))
                {
                    List<string> list = new List<string>();


                    StringReader sr = new StringReader(Value);

                    string str = sr.ReadLine();
                    while (str != null)
                    {
                        list.Add(str.Trim());
                        str = sr.ReadLine();
                    }
                    return (T)(object)list;

                }
                else if (typeof(T) == typeof(List<String>))
                {
                    List<String> list = new List<String>();


                    StringReader sr = new StringReader(Value);

                    String str = sr.ReadLine();
                    while (str != null)
                    {
                        list.Add(str.Trim());
                        str = sr.ReadLine();
                    }
                    return (T)(object)list;

                }

                else if (typeof(T) == typeof(double))
                {
                    double result = (double)defaultValue;
                    var tmp = Value;

                    tmp = tmp.Replace(",", ".");

                    result = double.Parse(tmp, System.Globalization.CultureInfo.InvariantCulture);
                    return (T)(object)result;

                }

                else if (typeof(T) == typeof(bool))
                {
                    bool result = bool.Parse(Value);
                    return (T)(object)result;

                }

                else if (typeof(T) == typeof(System.Windows.Media.Color))
                {

                    return (T)(object)System.Windows.Media.ColorConverter.ConvertFromString(Value);


                }

                else if (typeof(T) == typeof(System.Drawing.Color))
                {

                    return (T)(object)System.Drawing.ColorTranslator.FromHtml(Value);

                }
                else if (typeof(T) == typeof(SettingEnumValue))
                {
                    var enumValue = new SettingEnumValue();
                    if (string.IsNullOrEmpty(Value))
                    {
                        if (defaultValue is string || defaultValue is String)
                            enumValue.Value = defaultValue as string;
                        else
                            return (T)(object)defaultValue;
                    }
                    else
                        enumValue.Value = Value;
                    enumValue.Text = getEnumText(Value);
                    return (T)(object)enumValue;
                }

                else if (typeof(T) == typeof(Brush))
                {

                    return (T)(object)defaultValue; // TODO !!!

                }
                else if (typeof(T) == typeof(Dictionary<string, string>))
                {
                    var dictionary = new Dictionary<string, string>();

                    if (!string.IsNullOrEmpty(Value))
                    {
                        var xs = new XmlSerializer(typeof(List<SettingKeyValueItem>));
                        var sr = new StringReader(Value);
                        var tempList = (List<SettingKeyValueItem>)xs.Deserialize(sr);

                        foreach (var keyValueItem in tempList)
                        {
                            dictionary.Add(keyValueItem.Key, keyValueItem.Value);
                        }
                    }
                    return (T)(object)dictionary;

                }

                else if (typeof(T) == typeof(SettingFont))
                {
                    var font = new SettingFont();
                    font.Name = Value;
                    return (T)(object)font;
                }
                else
                    throw new NotSupportedException("Not supported type!");
            }
            catch (NotSupportedException ex)
            {
                throw ex;
            }
            catch
            {
                return (T)(object)defaultValue;
            }
        }

        private string getEnumText(string Value)
        {
            foreach (var item in Values)
            {
                if (item.Value == Value)
                    return item.Text;
            }
            return Value;
        }

        public void SetValue(object value)
        {
            if (value == null) return;

            type = value.GetType();
            SetSettingsType();

            if (value is int)
                Value = value.ToString();
            else if (value is uint)
                Value = value.ToString();

            else if (value is string)
                Value = (string)value;

            else if (value is String)
                Value = (String)value;

            else if (value is double)
                Value = value.ToString();

            else if (value is bool)
                Value = value.ToString();

            else if (value is System.Windows.Media.Color)
            {
                Value = ((System.Windows.Media.Color)value).ToString();
            }

            else if (value is List<string>)
            {
                StringBuilder sb = new StringBuilder();

                foreach (var item in (List<string>)value)
                {
                    sb.AppendLine(item);
                }
                Value = sb.ToString();
            }

            else if (value is List<String>)
            {
                StringBuilder sb = new StringBuilder();

                foreach (var item in (List<String>)value)
                {
                    sb.AppendLine(item);
                }
                Value = sb.ToString();
            }

            else if (value is System.Drawing.Color)
                Value = System.Drawing.ColorTranslator.ToHtml((System.Drawing.Color)value);

            else if (value is SettingEnumValue)
                Value = (value as SettingEnumValue).Value;
            else if (value.GetType().IsEnum)
                Value = value.ToString();

            else if (value is Brush)
            {
                try
                {
                    Value = string.Empty; // TODO !!!
                }
                catch
                {
                    Value = string.Empty;
                }

            }
            else if (value is Dictionary<string, string>)
            {
                try
                {
                    var tempdataitems = new List<SettingKeyValueItem>();

                    foreach (string key in (value as Dictionary<string, string>).Keys)
                    {
                        tempdataitems.Add(new SettingKeyValueItem(key, (value as Dictionary<string, string>)[key].ToString()));
                    }

                    XmlSerializer serializer = new XmlSerializer(typeof(List<SettingKeyValueItem>));
                    StringWriter sw = new StringWriter();
                    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                    ns.Add("", "");

                    serializer.Serialize(sw, tempdataitems, ns);
                    Value = sw.ToString();
                }
                catch
                {
                    Value = string.Empty;
                }
            }

            else if (value is SettingFont)
            {
                Value = (value as SettingFont).Name;
            }
            else
                throw new NotSupportedException("Not supported type!");
        }






        #region INotifyPropertyChanged Member

        public event PropertyChangedEventHandler PropertyChanged;
        private void onPropertyChanged(string property)
        {
            var propertyChanged = PropertyChanged;
            if (propertyChanged != null)
                propertyChanged(this, new PropertyChangedEventArgs(property));
        }

        #endregion
    }


    internal class AppsList : List<AppSetting>
    {
        public AppSetting this[string index]
        {
            get
            {
                var found = this.Where(a => a.AppId == index).FirstOrDefault();

                if (found == null)
                {
                    found = new AppSetting()
                    {
                        AppId = index,
                        DefaultSettings = new SettingsList(index),
                        UserSettings = new SettingsList(index)
                    };
                    this.Add(found);
                }

                return found;
            }
            set
            {
                var found = this.Where(a => a.AppId == index).FirstOrDefault();

                if (found != null)
                    this.Remove(found);

                this.Add(value);
            }
        }

        private void applySettings(Setting setting, XElement settingNode, XElement locNode)
        {

            var innerXML = String.Concat(settingNode.Nodes().Select(x => x.ToString()).ToArray()); //InnerXML
            setting.Value = System.Net.WebUtility.HtmlDecode(innerXML);

            if (settingNode.Attribute("type") != null)
                setting.Type = settingNode.Attribute("type").Value;

            if (settingNode.Attribute("group") != null)
            {
                if (!string.IsNullOrEmpty(settingNode.Attribute("group").Value))
                {
                    setting.Group = settingNode.Attribute("group").Value;
                    var localizedGroupString = Settings.GetLocalizedString(locNode, settingNode.Attribute("group").Value);
                    if (!string.IsNullOrEmpty(setting.GroupName))
                    {
                        if (setting.GroupName == setting.Group || localizedGroupString != setting.Group)
                            setting.GroupName = localizedGroupString;
                    }
                    else
                        setting.GroupName = localizedGroupString;
                }
                else
                {
                    setting.GroupName = Properties.Resources.strDefaultGroupName;
                    setting.Group = setting.GroupName;
                }
            }
            else
            {
                setting.GroupName = Properties.Resources.strDefaultGroupName;
                setting.Group = setting.GroupName;
            }

            var localizedString = Settings.GetLocalizedString(locNode, setting.Key);
            if (!string.IsNullOrEmpty(setting.DisplayName))
            {
                if (setting.DisplayName == setting.Key || localizedString != setting.Key)
                    setting.DisplayName = localizedString;
            }
            else
                setting.DisplayName = localizedString;

            if (settingNode.Attribute("scope") != null)
            {
                var value = settingNode.Attribute("scope").Value;
                if (string.IsNullOrEmpty(value))
                {
                    setting.Scope = SettingScope.Internal;
                }
                else
                {
                    value = value.ToLower();
                    if (value == "advanced")
                        setting.Scope = SettingScope.Advanced;
                    else if (value == "default")
                        setting.Scope = SettingScope.Standard;
                    else
                        setting.Scope = SettingScope.Internal;
                }
            }

            if (settingNode.Attribute("values") != null)
            {
                var value = settingNode.Attribute("values").Value;
                setting.values = value;
                if (!string.IsNullOrEmpty(value))
                {
                    var values = value.Split(',');
                    setting.Values.Clear();
                    foreach (var item in values)
                    {
                        var enumValue = new SettingEnumValue();
                        enumValue.Value = item;
                        enumValue.Text = Settings.GetLocalizedString(locNode, item);
                        setting.Values.Add(enumValue);
                    }
                }
            }
        }

        public bool LoadUserSettings(string fileName)
        {
            try
            {
                var appsList = this;

                XDocument xmldoc = XDocument.Load(fileName);

                var root = xmldoc.Element("Settings");
                if (root != null)
                {
                    // Check Settings Version
                    var version = root.Attribute("version");
                    if (version == null || version.Value != Settings.Version)
                    {
                        Log.Error("Wrong Settings version: " + fileName);
                        return false;
                    }
                }

                var apps = from application in xmldoc.Descendants("App") select application;

                foreach (var app in apps)
                {
                    var attributeAppId = app.Attribute("appID");
                    if (attributeAppId != null && !string.IsNullOrEmpty(attributeAppId.Value))
                    {
                        var localizationNode = app.Element("Localization");
                        var settings = from setting in app.Descendants("Setting") select setting;
                        appsList[attributeAppId.Value].DefaultSettings.ApplyLocalization(localizationNode);

                        foreach (var setting in settings)
                        {
                            var attributeKey = setting.Attribute("key");
                            if (attributeKey != null && !string.IsNullOrEmpty(attributeKey.Value))
                            {
                                applySettings(appsList[attributeAppId.Value].UserSettings[attributeKey.Value], setting, localizationNode);
                            }
                        }
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        public bool Save(string fileName)
        {
            XElement root = new XElement("Settings", new XAttribute("version", "1"));


            XElement apps = new XElement("Apps");

            foreach (var app in this)
            {
                XElement appNode = new XElement("App",
                   new XAttribute("appID", app.AppId));


                foreach (var setting in app.UserSettings)
                {
                    if (setting.Value == null) continue;
                    if (setting.Key == null) continue;
                    if (setting.Value == app.DefaultSettings[setting.Key].Value) continue;

                    setting.SetSettingsType();
                    XElement settingNode = null;

                    settingNode = new XElement("Setting",
                         new XAttribute("key", setting.Key),
                         new XAttribute("type", setting.Type));

                    settingNode.Value = setting.Value;

                    appNode.Add(settingNode);
                }

                apps.Add(appNode);
            }

            root.Add(apps);

            try
            {
                root.Save(fileName);
                return true;
            }
            catch
            {
                return false;
            }
        }
    }

    public class SettingsList : List<Setting>
    {
        private string appId;

        internal SettingsList(string appId)
        {
            this.appId = appId;
            Localization = new Dictionary<string, Dictionary<string, string>>();
        }

        internal Dictionary<string, Dictionary<string, string>> Localization { get; set; }

        public Setting this[string index, string instance]
        {
            get
            {
                var found = this.Where(s => s.Key == index).FirstOrDefault();

                if (found == null)
                {
                    found = new Setting()
                    {
                        Key = index,
                        AppID = appId
                    };
                    this.Add(found);
                }

                return found;
            }
            set
            {
                var found = this.Where(s => s.Key == index).FirstOrDefault();

                if (found != null)
                    this.Remove(found);
                this.Add(value);
            }
        }
        public Setting this[string index]
        {
            get
            {
                var found = this.Where(s => s.Key == index).FirstOrDefault();

                if (found == null)
                {
                    found = new Setting()
                    {
                        Key = index,

                        AppID = appId
                    };
                    this.Add(found);
                }

                return found;
            }
            set
            {
                var found = this.Where(s => s.Key == index).FirstOrDefault();

                if (found != null)
                    this.Remove(found);
                this.Add(value);
            }
        }

        internal void ApplyLocalization(XElement locNode)
        {
            if (locNode == null) return;
            foreach (var resources in locNode.Elements())
            {
                var attr = resources.Attribute("lang");
                if (attr != null)
                {
                    try
                    {
                        if (!Localization.ContainsKey(attr.Value))
                            Localization.Add(attr.Value, new Dictionary<string, string>());
                    }
                    catch
                    {
                        // key already exists
                    }
                    foreach (var resource in resources.Elements())
                    {
                        var key = resource.Attribute("key");
                        var text = resource.Attribute("text");
                        if (key != null && text != null)
                        {
                            try
                            {
                                if (!Localization[attr.Value].ContainsKey(key.Value))
                                    Localization[attr.Value].Add(key.Value, text.Value);
                            }
                            catch
                            {
                                // key already exists
                            }
                        }
                    }
                }
            }
        }


    }

    public class AppSetting
    {
        internal string AppId { get; set; }
        public SettingsList UserSettings { get; internal set; }
        public SettingsList DefaultSettings { get; internal set; }

        public bool IsSettingsAreEqualDefaults
        {
            get
            {
                foreach (var setting in UserSettings)
                {
                    if (setting.Value == null) continue;
                    if (setting.Key == null) continue;
                    if (setting.Value != DefaultSettings[setting.Key].Value) return false;
                }


                return true;
            }
        }

        private void applySettings(Setting setting, XElement settingNode, XElement locNode)
        {
            var attributeType = settingNode.Attribute("type");


            // Apply value if setting not existed before, or no value found, or if Type or Values changed
            if (string.IsNullOrEmpty(setting.Value) || (attributeType != null && attributeType.Value != setting.Type))
            {
                var innerXML = String.Concat(settingNode.Nodes().Select(x => x.ToString()).ToArray()); //InnerXML
                setting.Value = System.Net.WebUtility.HtmlDecode(innerXML);
            }

            if (attributeType != null)
                setting.Type = settingNode.Attribute("type").Value;

            if (settingNode.Attribute("group") != null)
            {
                if (!string.IsNullOrEmpty(settingNode.Attribute("group").Value))
                {
                    setting.Group = settingNode.Attribute("group").Value;

                    var localizedGroupString = Settings.GetLocalizedString(locNode, settingNode.Attribute("group").Value);
                    if (!string.IsNullOrEmpty(setting.GroupName))
                    {
                        if (setting.GroupName == setting.Group || localizedGroupString != setting.Group)
                            setting.GroupName = localizedGroupString;
                    }
                    else
                        setting.GroupName = localizedGroupString;
                }
                else
                {
                    setting.GroupName = Properties.Resources.strDefaultGroupName;
                    setting.Group = setting.GroupName;
                }
            }
            else
            {
                setting.GroupName = Properties.Resources.strDefaultGroupName;
                setting.Group = setting.GroupName;
            }

            var localizedString = Settings.GetLocalizedString(locNode, setting.Key);
            if (!string.IsNullOrEmpty(setting.DisplayName))
            {
                if (setting.DisplayName == setting.Key || localizedString != setting.Key)
                    setting.DisplayName = localizedString;
            }
            else
                setting.DisplayName = localizedString;

            if (settingNode.Attribute("scope") != null)
            {
                var value = settingNode.Attribute("scope").Value;
                if (string.IsNullOrEmpty(value))
                {
                    setting.Scope = SettingScope.Internal;
                }
                else
                {
                    value = value.ToLower();
                    if (value == "advanced")
                        setting.Scope = SettingScope.Advanced;
                    else if (value == "default")
                        setting.Scope = SettingScope.Standard;
                    else
                        setting.Scope = SettingScope.Internal;
                }
            }

            if (settingNode.Attribute("values") != null)
            {
                var value = settingNode.Attribute("values").Value;
                setting.values = value;
                if (!string.IsNullOrEmpty(value))
                {
                    var values = value.Split(',');
                    setting.Values.Clear();
                    foreach (var item in values)
                    {
                        var enumValue = new SettingEnumValue();
                        enumValue.Value = item;
                        enumValue.Text = Settings.GetLocalizedString(locNode, item);
                        setting.Values.Add(enumValue);
                    }
                }
            }
        }

        internal void RemoveOldSettings()
        {
            var removeList = new List<Setting>();

            foreach (var setting in UserSettings)
            {
                var found = DefaultSettings.Where(s => s.Key == setting.Key).FirstOrDefault();
                if (found == null)
                {
                    // UserSetting does not exists anymore
                    removeList.Add(setting);
                }
            }

            // Remove old settings
            foreach (var setting in removeList)
            {
                UserSettings.Remove(setting);
            }
        }

        internal bool LoadDefaultSettings(string fileName)
        {
            try
            {
                var xmldoc = XDocument.Load(fileName);

                var root = xmldoc.Element("Settings");
                if (root != null)
                {
                    // Check Settings Version
                    var version = root.Attribute("version");
                    if (version == null || version.Value != Settings.Version)
                    {
                        Log.Error("Wrong Settings version: " + fileName);
                        return false;
                    }
                }

                var localizationNode = root.Element("Localization");
                var settings = from setting in root.Descendants("Setting") select setting;

                DefaultSettings.ApplyLocalization(localizationNode);

                foreach (var setting in settings)
                {
                    var attributeKey = setting.Attribute("key");
                    if (attributeKey != null && !string.IsNullOrEmpty(attributeKey.Value))
                    {
                        applySettings(DefaultSettings[attributeKey.Value], setting, localizationNode);
                        applySettings(UserSettings[attributeKey.Value], setting, localizationNode);
                    }
                }

                return true;
            }
            catch
            {
                return false;
            }
        }
    }


    public class SettingChangedEventArgs : EventArgs
    {
        public string AppId { get; set; }
        public string Setting { get; set; }
    }

    public class SettingEventArgs : EventArgs
    {
        public Setting Setting { get; set; }

        public SettingEventArgs(Setting setting)
        {
            Setting = setting;
        }
    }

    public delegate void SettingChangedEventHandler(object sender, SettingChangedEventArgs e);
    public delegate string CustomSettingDisplayNameRequestEventHandler(object sender, SettingEventArgs e);
    public delegate CustomSettingsDialog CustomSettingDialogRequestEventHandler(object sender, SettingEventArgs e);


    public enum SettingScope
    {
        Internal, Standard, Advanced
    }

    public class SettingEnumValue
    {
        public string Value { get; set; }
        public string Text { get; set; }
    }


    public class SettingFont
    {
        public string Name { get; set; }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            var tmp = obj;
            if (tmp is SettingFont)
            {
                return (tmp as SettingFont).Name == this.Name;
            }
            return false;
        }
    }

    public class SettingKeyValueItem
    {
        public string Key { get; set; }
        public string Value { get; set; }

        public SettingKeyValueItem(string key, string value)
        {
            Key = key;
            Value = value;
        }

        public SettingKeyValueItem()
        {

        }
    }
}
